package pres.lnk.learn.thread;

import org.junit.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * 主线程捕获子线程异常
 *
 * @Author lnk
 * @Date 2018/1/30
 */
public class ThreadTryChildrenException {

    /**
     * <p>方式一，实现 Thread.UncaughtExceptionHandler 接口</p>
     */
    @Test
    public void test1() throws InterruptedException {
        Thread.UncaughtExceptionHandler childrenException = new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println(t.getName() + " 出现异常：" + e.getMessage());
            }
        };
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            Thread t = new Thread("线程" + finalI) {
                @Override
                public void run() {
                    if (finalI % 2 != 0) {
                        throw new RuntimeException(finalI + " 不是偶数");
                    }
                }
            };
            t.setUncaughtExceptionHandler(childrenException);
            t.start();
        }

        //主线程需要等子线程执行完再线束，不然后主线程已结束子线程还没开始，看不到测试结果
        Thread.sleep(1000);
    }

    /**
     * <p>方式二，通过继承ThreadGroup重写uncaughtException方法捕获</p>
     */
    @Test
    public void test2() throws InterruptedException {
        ThreadGroup group = new ThreadGroup("tryException"){
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println(t.getName() + " 出现异常：" + e.getMessage());
            }
        };

        for (int i = 0; i < 5; i++) {
            int finalI = i;
            Thread t = new Thread(group,"线程" + finalI) {
                @Override
                public void run() {
                    if (finalI % 2 != 0) {
                        throw new RuntimeException(finalI + " 不是偶数");
                    }
                }
            };
            t.start();
        }

        //主线程需要等子线程执行完再线束，不然后主线程已结束子线程还没开始，看不到测试结果
        Thread.sleep(1000);
    }

    /**
     * <p>线程池捕获异常</p>
     */
    @Test
    public void test3() throws InterruptedException {
        Thread[] array = new Thread[5];
        ExecutorService fixedPool = Executors.newFixedThreadPool(array.length, new MyThreadFactory());

        for (int i = 0; i < 5; i++) {
            int finalI = i;
            array[i] = new Thread("线程" + finalI) {
                @Override
                public void run() {
                    if (finalI % 2 != 0) {
                        throw new RuntimeException(finalI + " 不是偶数");
                    }
                }
            };
            fixedPool.execute(array[i]);
        }
        fixedPool.shutdown();

        //等待子线程执行完成
        Thread.sleep(1000);
    }

    /**
     * 线程池的线程异常处理器
     */
    private static class MyThreadFactory implements ThreadFactory {
        private Thread.UncaughtExceptionHandler exceptionHandler = new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println(t.getName() + " 出现异常：" + e.getMessage());
            }
        };

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setUncaughtExceptionHandler(exceptionHandler);
            return t;
        }
    }

    /**
     * <p>使用线程池捕获异常要注意一个问题</p>
     */
    @Test
    public void test4() throws InterruptedException {
        ExecutorService fixedPool = Executors.newFixedThreadPool(1);

        Thread.UncaughtExceptionHandler childrenException = new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("这句话不会被打印出来");
            }
        };

        Thread t = new Thread(){
            @Override
            public void run() {
                throw new RuntimeException("我就没事抛个异常");
            }
        };
        t.setUncaughtExceptionHandler(childrenException);

        /*
         * 使用线程池时，线程原来的UncaughtExceptionHandler处理器将会失效
         */
        fixedPool.execute(t);
        fixedPool.shutdown();
        //如果直接启动线程则UncaughtExceptionHandler处理器能捕获到异常
//        t.start();

        //等待子线程执行完成
        Thread.sleep(1000);
    }
}
